using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace ICP_MS.Plugin.Tune;

public class HalfAutoTuneExperiment
{
	public class HalfTuneIsotope
	{
		public int Id { get; set; }

		public string Symbol { get; set; }

		public double Mass { get; set; }

		public int Dwell { get; set; }

		public double Result { get; set; }
	}

	public class HalfTuneRatio
	{
		public int Id { get; set; }

		public HalfTuneIsotope Numerator { get; set; }

		public HalfTuneIsotope Denominator { get; set; }

		public int Dwell { get; set; }
	}

	public class HalfDeviceCondition
	{
		public int Id { get; set; }

		public bool Selected { get; set; }

		public bool Force { get; set; }

		public string Device { get; set; }

		public double MinValue { get; set; }

		public double MaxValue { get; set; }

		public double DefaultValue { get; set; }

		public double ReadErr { get; set; }

		public double FinishErr { get; set; }

		public double BestValue { get; set; }

		public bool Used { get; set; }

		public HalfDeviceCondition Clone()
		{
			HalfDeviceCondition device = new HalfDeviceCondition();
			device.Id = Id;
			device.Device = Device;
			device.Selected = Selected;
			device.Force = Force;
			device.MinValue = MinValue;
			device.MaxValue = MaxValue;
			device.DefaultValue = DefaultValue;
			device.ReadErr = ReadErr;
			device.FinishErr = FinishErr;
			return device;
		}

		public void Copy(HalfDeviceCondition dev)
		{
			if (Id == dev.Id)
			{
				Force = dev.Force;
				MinValue = dev.MinValue;
				MaxValue = dev.MaxValue;
				DefaultValue = dev.DefaultValue;
				ReadErr = dev.ReadErr;
				FinishErr = dev.FinishErr;
			}
		}
	}

	private List<double> massX = new List<double>();

	private Thread threadTune = null;

	private AutoResetEvent scanEvent = new AutoResetEvent(initialState: false);

	public List<HalfTuneIsotope> Isotopes { get; set; }

	public List<HalfTuneRatio> Ratios { get; set; }

	public List<HalfDeviceCondition> Devices { get; set; }

	public string Name { get; set; }

	public string Description { get; set; }

	public string Solution { get; set; }

	public bool isTuning { get; set; }

	public bool TuneSucceed { get; set; }

	public event EventHandler EventTuneFinish;

	public event EventHandler<HalfMsgEventArgs> EventTuneMsg;

	public event EventHandler<ResultEventArgs> EventTuneResult;

	public HalfAutoTuneExperiment()
	{
		Isotopes = new List<HalfTuneIsotope>();
		Ratios = new List<HalfTuneRatio>();
		Devices = new List<HalfDeviceCondition>();
	}

	public void InitTuneDevices()
	{
		if (Devices == null || Devices.Count == 0)
		{
			Devices.Add(new HalfDeviceCondition
			{
				Id = 0,
				Device = Lang.Txt("HalfAutoTuneExperiment.XPosition.Text"),
				Force = true,
				MaxValue = 16.0,
				MinValue = 2.0,
				DefaultValue = 5.0,
				ReadErr = 0.05,
				FinishErr = 0.5
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 1,
				Device = Lang.Txt("HalfAutoTuneExperiment.YPosition.Text"),
				Force = true,
				MaxValue = 16.0,
				MinValue = 2.0,
				DefaultValue = 5.0,
				ReadErr = 0.05,
				FinishErr = 0.5
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 2,
				Device = Lang.Txt("HalfAutoTuneExperiment.ZPosition.Text"),
				Force = true,
				MaxValue = 16.0,
				MinValue = 2.0,
				DefaultValue = 5.0,
				ReadErr = 0.05,
				FinishErr = 0.5
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 3,
				Device = Lang.Txt("HalfAutoTuneExperiment.ExtractionLens.Text"),
				Force = true,
				MaxValue = -100.0,
				MinValue = -720.0,
				DefaultValue = -470.0,
				ReadErr = 0.5,
				FinishErr = 10.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 4,
				Device = Lang.Txt("HalfAutoTuneExperiment.Lens1.Text"),
				Force = true,
				MaxValue = -500.0,
				MinValue = -1800.0,
				DefaultValue = -1200.0,
				ReadErr = 0.5,
				FinishErr = 10.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 5,
				Device = Lang.Txt("HalfAutoTuneExperiment.Lens2.Text"),
				Force = true,
				MaxValue = -70.0,
				MinValue = -100.0,
				DefaultValue = -80.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 6,
				Device = Lang.Txt("HalfAutoTuneExperiment.Focus.Text"),
				Force = true,
				MaxValue = 8.0,
				MinValue = 18.0,
				DefaultValue = 13.5,
				ReadErr = 0.5,
				FinishErr = 0.5
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 7,
				Device = Lang.Txt("HalfAutoTuneExperiment.D1.Text"),
				Force = true,
				MaxValue = -10.0,
				MinValue = -200.0,
				DefaultValue = -150.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 8,
				Device = Lang.Txt("HalfAutoTuneExperiment.D2.Text"),
				Force = true,
				MaxValue = -10.0,
				MinValue = -60.0,
				DefaultValue = -25.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 9,
				Device = Lang.Txt("HalfAutoTuneExperiment.QE.Text"),
				Force = true,
				MaxValue = -10.0,
				MinValue = -60.0,
				DefaultValue = -35.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 10,
				Device = Lang.Txt("HalfAutoTuneExperiment.HexPolebias.Text"),
				Force = true,
				MaxValue = -20.0,
				MinValue = -30.0,
				DefaultValue = -25.0,
				ReadErr = 0.5,
				FinishErr = 1.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 11,
				Device = Lang.Txt("HalfAutoTuneExperiment.Lens3.Text"),
				Force = true,
				MaxValue = -150.0,
				MinValue = -200.0,
				DefaultValue = -180.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 12,
				Device = Lang.Txt("HalfAutoTuneExperiment.DA.Text"),
				Force = true,
				MaxValue = -20.0,
				MinValue = -100.0,
				DefaultValue = -60.0,
				ReadErr = 0.5,
				FinishErr = 5.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 13,
				Device = Lang.Txt("HalfAutoTuneExperiment.NebulizerGas.Text"),
				Force = true,
				MaxValue = 1.5,
				MinValue = 0.9,
				DefaultValue = 1.1,
				ReadErr = 0.1,
				FinishErr = 0.1
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 14,
				Device = Lang.Txt("HalfAutoTuneExperiment.CoolGas.Text"),
				Force = true,
				MaxValue = 16.0,
				MinValue = 10.0,
				DefaultValue = 13.0,
				ReadErr = 0.1,
				FinishErr = 1.0
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 15,
				Device = Lang.Txt("HalfAutoTuneExperiment.NebulizerGas.Text"),
				Force = true,
				MaxValue = 1.6,
				MinValue = 0.5,
				DefaultValue = 0.8,
				ReadErr = 0.1,
				FinishErr = 0.1
			});
			Devices.Add(new HalfDeviceCondition
			{
				Id = 16,
				Device = Lang.Txt("HalfAutoTuneExperiment.CollisionGas.Text"),
				Force = false,
				MaxValue = 3.0,
				MinValue = 0.0,
				DefaultValue = 1.5,
				ReadErr = 0.1,
				FinishErr = 0.1
			});
		}
	}

	public void InitIsotopes()
	{
		if (Isotopes != null && Isotopes.Count() == 0)
		{
			Isotopes.Add(new HalfTuneIsotope
			{
				Symbol = "Be",
				Mass = 9.01218,
				Dwell = 10
			});
		}
		Isotopes.Add(new HalfTuneIsotope
		{
			Symbol = "Co",
			Mass = 58.93319,
			Dwell = 10
		});
		Isotopes.Add(new HalfTuneIsotope
		{
			Symbol = "Y",
			Mass = 88.90585,
			Dwell = 10
		});
		Isotopes.Add(new HalfTuneIsotope
		{
			Symbol = "In",
			Mass = 114.90387,
			Dwell = 10
		});
		Isotopes.Add(new HalfTuneIsotope
		{
			Symbol = "Ce",
			Mass = 139.90544,
			Dwell = 10
		});
		Isotopes.Add(new HalfTuneIsotope
		{
			Symbol = "Bi",
			Mass = 208.98038,
			Dwell = 10
		});
		Isotopes.Add(new HalfTuneIsotope
		{
			Symbol = "CeO",
			Mass = 151.90204,
			Dwell = 10
		});
		Isotopes.Add(new HalfTuneIsotope
		{
			Symbol = "Ce++",
			Mass = 69.9527205,
			Dwell = 10
		});
	}

	public void InitRatios()
	{
		if (Ratios != null && Ratios.Count() == 0)
		{
			Ratios.Add(new HalfTuneRatio
			{
				Numerator = Isotopes.Where((HalfTuneIsotope isotope) => isotope.Symbol == "Ce++").First(),
				Denominator = Isotopes.Where((HalfTuneIsotope isotope) => isotope.Symbol == "Ce").First(),
				Dwell = 10
			});
			Ratios.Add(new HalfTuneRatio
			{
				Numerator = Isotopes.Where((HalfTuneIsotope isotope) => isotope.Symbol == "CeO").First(),
				Denominator = Isotopes.Where((HalfTuneIsotope isotope) => isotope.Symbol == "Ce").First(),
				Dwell = 10
			});
		}
	}

	public bool CheckPosition(double value, HalfDeviceCondition dev)
	{
		switch (dev.Id)
		{
		case 0:
			if (Math.Abs(PlatManager.plat.ReadX / 100.0 - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 1:
			if (Math.Abs(PlatManager.plat.ReadZ / 100.0 - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 2:
			if (Math.Abs(PlatManager.plat.ReadY / 100.0 - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 3:
			if (Math.Abs(LensManager.x2lens.ReadEx - value) <= Math.Abs(value) * 0.5)
			{
				return true;
			}
			break;
		case 4:
			if (Math.Abs(LensManager.x2lens.ReadL1 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 5:
			if (Math.Abs(LensManager.x2lens.ReadL2 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 6:
			if (Math.Abs(LensManager.x2lens.ReadF - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 7:
			if (Math.Abs(LensManager.x2lens.ReadD1 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 8:
			if (Math.Abs(LensManager.x2lens.ReadD2 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 9:
			if (Math.Abs(LensManager.x2lens.ReadQE - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 10:
			if (Math.Abs(LensManager.x2lens.ReadPole - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 11:
			if (Math.Abs(LensManager.x2lens.ReadL3 - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 12:
			if (Math.Abs(LensManager.x2lens.ReadDA - value) <= Math.Abs(value) * dev.ReadErr)
			{
				return true;
			}
			break;
		case 13:
			if (Math.Abs(PlasmaManager.plasma.ReadNebuliser - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 14:
			if (Math.Abs(PlasmaManager.plasma.ReadCool - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 15:
			if (Math.Abs(PlasmaManager.plasma.ReadAuxiliary - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		case 16:
			if (Math.Abs(PlasmaManager.plasma.ReadCollision - value) <= value * dev.ReadErr)
			{
				return true;
			}
			break;
		}
		return false;
	}

	public void StartTune()
	{
		Detector.finishReceive += Detector_finishReceive;
		isTuning = true;
		threadTune = new Thread(ThreadTune);
		threadTune.Start();
	}

	private void Detector_errorReceive(object sender, ErrorMsgEventArgs e)
	{
		LensManager.x2lens.Etp = 0.0;
		LensManager.Instance.SetEtp();
		StopTune();
		MessageBox.Show("质量数为" + e.X + "元素浓度过高，请检查，已停止测试！", "警告-倍增器保护");
	}

	public void StopTune()
	{
		isTuning = false;
		Detector.finishReceive -= Detector_finishReceive;
		if (threadTune != null && threadTune.IsAlive)
		{
			threadTune.Abort();
		}
		Detector.Instance.Stop();
		Detector.Instance.ExperimentState = -1;
	}

	private void Detector_finishReceive(object sender, EventArgs e)
	{
		scanEvent.Set();
	}

	private void ThreadTune()
	{
		TuneMsg("Starting Tune");
		InitScanDatas();
		HalfDeviceCondition device = Devices.Find((HalfDeviceCondition r) => r.Selected);
		TuneMsg($"Tuning {device.Device}");
		if (!Tune(device))
		{
			TuneMsg("Scan error");
			TuneFinish(error: true);
		}
		else
		{
			TuneFinish(error: false);
		}
	}

	private void TuneFinish(bool error)
	{
		TuneSucceed = !error;
		if (this.EventTuneFinish != null)
		{
			this.EventTuneFinish(null, null);
		}
	}

	private void TuneResult(double devicevalue, double[] mass, double[] intensity)
	{
		if (this.EventTuneMsg != null)
		{
			this.EventTuneResult(null, new ResultEventArgs(devicevalue, mass, intensity));
		}
	}

	private void TuneMsg(string message)
	{
		if (this.EventTuneMsg != null)
		{
			this.EventTuneMsg(null, new HalfMsgEventArgs(message));
		}
	}

	private bool Tune(HalfDeviceCondition device)
	{
		double min = device.MinValue;
		double max = device.MaxValue;
		double currentvalue = min;
		do
		{
			if (!TuneDevice(device, currentvalue))
			{
				return false;
			}
			currentvalue += device.FinishErr;
		}
		while (currentvalue <= max);
		return true;
	}

	private bool TuneDevice(HalfDeviceCondition device, double value)
	{
		TuneMsg($"{device.Device} set to {value:0.00}");
		SetDeviceValue(device.Id, value);
		int count = 0;
		while (!CheckPosition(value, device))
		{
			count++;
			Thread.Sleep(1000);
			if (count >= 10)
			{
				MessageBox.Show($"设置调谐参数{device.Device}失败");
				return false;
			}
		}
		Thread.Sleep(2000);
		Detector.msDetectorPara.InitIntensity();
		List<int> res = new List<int>();
		for (int i = 0; i < massX.Count; i++)
		{
			res.Add(1);
		}
		Detector.Instance.SetMassAxis(massX.ToArray());
		Detector.Instance.SendHopDatas(massX.ToArray(), res.ToArray(), true, false);
		if (!scanEvent.WaitOne(30000))
		{
			return false;
		}
		CalculateResults(device, value);
		return true;
	}

	private void CalculateResults(HalfDeviceCondition device, double value)
	{
		double[] mass = Detector.msDetectorPara.MassAxis.ToArray();
		double[] intensity = Detector.msDetectorPara.IntesnsityPulse.ToArray();
		TuneResult(value, mass, intensity);
	}

	private void InitScanDatas()
	{
		Isotopes.Sort((HalfTuneIsotope i1, HalfTuneIsotope i2) => i1.Mass.CompareTo(i2.Mass));
		massX.Clear();
		foreach (HalfTuneIsotope isotope in Isotopes)
		{
			massX.Add(isotope.Mass);
		}
	}

	public bool SaveConfig()
	{
		string name = DateTime.Now.ToString("yyyy_MM_dd_hh_mm_ss");
		string path = Application.StartupPath + "\\HalfAutotune\\" + name + ".xml";
		XMLFile.Save(path, this);
		return true;
	}

	public bool DeleteConfig(string name)
	{
		string path = Application.StartupPath + "\\HalfAutotune\\" + name + ".xml";
		if (File.Exists(path))
		{
			File.Delete(path);
		}
		return true;
	}

	public static string[] LoadAllConfigs()
	{
		string dir = Application.StartupPath + "\\HalfAutotune";
		if (!Directory.Exists(dir))
		{
			Directory.CreateDirectory(dir);
		}
		return Directory.GetFiles(dir, "*.xml", SearchOption.TopDirectoryOnly);
	}

	public static void DeleteConfigs(string name)
	{
		string path = Application.StartupPath + "\\HalfAutotune\\" + name + ".xml";
		if (File.Exists(path))
		{
			File.Delete(path);
		}
	}

	public static HalfAutoTuneExperiment LoadConfig(string name)
	{
		HalfAutoTuneExperiment experiment = null;
		string path = Application.StartupPath + "\\HalfAutotune\\" + name + ".xml";
		XMLFile.Open(path, ref experiment);
		return experiment;
	}

	private void SetDeviceValue(int id, double value)
	{
		switch (id)
		{
		case 0:
			PlatManager.plat.X = (float)value;
			Plasma.Instance.SetXAxis();
			break;
		case 1:
			PlatManager.plat.Z = (float)value;
			Plasma.Instance.SetZAxis();
			break;
		case 2:
			PlatManager.plat.Y = (float)value;
			Plasma.Instance.SetYAxis();
			break;
		case 3:
			LensManager.x2lens.Ex = value;
			LensManager.Instance.X2_SetEx();
			break;
		case 4:
			LensManager.x2lens.L1 = value;
			LensManager.Instance.X2_SetL1();
			break;
		case 5:
			LensManager.x2lens.L2 = value;
			LensManager.Instance.X2_SetL2();
			break;
		case 6:
			LensManager.x2lens.F = value;
			LensManager.Instance.X2_SetF();
			break;
		case 7:
			LensManager.x2lens.D1 = value;
			LensManager.Instance.X2_SetD1();
			break;
		case 8:
			LensManager.x2lens.D2 = value;
			LensManager.Instance.X2_SetD2();
			break;
		case 9:
			LensManager.x2lens.QE = value;
			LensManager.Instance.X2_SetQE();
			break;
		case 10:
			LensManager.x2lens.Pole = value;
			LensManager.Instance.X2_SetPole();
			break;
		case 11:
			LensManager.x2lens.L3 = value;
			LensManager.Instance.X2_SetL3();
			break;
		case 12:
			LensManager.x2lens.DA = value;
			LensManager.Instance.X2_SetDA();
			break;
		case 13:
			PlasmaManager.plasma.Nebuliser = value;
			Plasma.Instance.OpenNebulizerGas();
			break;
		case 14:
			PlasmaManager.plasma.Cool = value;
			Plasma.Instance.OpenCoolGas();
			break;
		case 15:
			PlasmaManager.plasma.Auxiliary = value;
			Plasma.Instance.OpenAuxiliaryGas();
			break;
		}
	}
}
